//@author A0091838U
//@The class define the Patterns of CrossOff Support Date Format
package crossoff.datetime;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.joda.time.LocalDate;

public class DateValidator {

	private static final int DEFAULT_YEAR = new LocalDate().getYear();
	private static final int SPECIAL_YEAR_GROUP = 27;
	private static final int YEAR_TO_ADD = 2000;
	private static final String DATE_PATTERN0 = "(0?[1-9]|[12][0-9]|3[01])/(0?[1-9]|1[012])/((19|20)\\d\\d)";
	private static final String DATE_PATTERN1 = "(0?[1-9]|[12][0-9]|3[01])-(0?[1-9]|1[012])-((19|20)\\d\\d)";
	private static final String DATE_PATTERN2 = "(0?[1-9]|[12][0-9]|3[01])/(0?[1-9]|1[012])/(1[3-9]|[2-9][0-9])";
	private static final String DATE_PATTERN3 = "(0?[1-9]|[12][0-9]|3[01])-(0?[1-9]|1[012])-(1[3-9]|[2-9][0-9])";
	private static final String DATE_PATTERN4 = "(0?[1-9]|[12][0-9]|3[01])/(0[1-9]|1[012])";
	private static final String DATE_PATTERN5 = "(0?[1-9]|[12][0-9]|3[01])-(0[1-9]|1[012])";
	private static final String DATE_PATTERN6 = "(0?[1-9]|[12][0-9]|3[01])\\s((?i)((Jan)|(January)|(Feb)|(February)|(Mar)|(March)|(Apr)|(April)|(May)|(Jun)|(June)|(Jul)|(July)|(Aug)|(August)|(Sep)|(September)|(Oct)|(October)|(Nov)|(November)|(Dec)|(December)))\\s(1[3-9]|[2-9][0-9])";
	private static final String DATE_PATTERN7 = "(0?[1-9]|[12][0-9]|3[01])-((?i)((Jan)|(January)|(Feb)|(February)|(Mar)|(March)|(Apr)|(April)|(May)|(Jun)|(June)|(Jul)|(July)|(Aug)|(August)|(Sep)|(September)|(Oct)|(October)|(Nov)|(November)|(Dec)|(December)))-(1[3-9]|[2-9][0-9])";
	private static final String DATE_PATTERN8 = "(0?[1-9]|[12][0-9]|3[01])/((?i)((Jan)|(January)|(Feb)|(February)|(Mar)|(March)|(Apr)|(April)|(May)|(Jun)|(June)|(Jul)|(July)|(Aug)|(August)|(Sep)|(September)|(Oct)|(October)|(Nov)|(November)|(Dec)|(December)))/((19|20)\\d\\d)";
	private static final String DATE_PATTERN9 = "(0?[1-9]|[12][0-9]|3[01])-((?i)((Jan)|(January)|(Feb)|(February)|(Mar)|(March)|(Apr)|(April)|(May)|(Jun)|(June)|(Jul)|(July)|(Aug)|(August)|(Sep)|(September)|(Oct)|(October)|(Nov)|(November)|(Dec)|(December)))-((19|20)\\d\\d)";
	private static final String DATE_PATTERN10 = "(0?[1-9]|[12][0-9]|3[01])/((?i)((Jan)|(January)|(Feb)|(February)|(Mar)|(March)|(Apr)|(April)|(May)|(Jun)|(June)|(Jul)|(July)|(Aug)|(August)|(Sep)|(September)|(Oct)|(October)|(Nov)|(November)|(Dec)|(December)))/(1[3-9]|[2-9][0-9])";
	private static final String DATE_PATTERN11 = "(0?[1-9]|[12][0-9]|3[01])\\s((?i)((Jan)|(January)|(Feb)|(February)|(Mar)|(March)|(Apr)|(April)|(May)|(Jun)|(June)|(Jul)|(July)|(Aug)|(August)|(Sep)|(September)|(Oct)|(October)|(Nov)|(November)|(Dec)|(December)))\\s((19|20)\\d\\d)";
	private static final String DATE_PATTERN12 = "(0?[1-9]|[12][0-9]|3[01])/((?i)((Jan)|(January)|(Feb)|(February)|(Mar)|(March)|(Apr)|(April)|(May)|(Jun)|(June)|(Jul)|(July)|(Aug)|(August)|(Sep)|(September)|(Oct)|(October)|(Nov)|(November)|(Dec)|(December)))";
	private static final String DATE_PATTERN13 = "(0?[1-9]|[12][0-9]|3[01])-((?i)((Jan)|(January)|(Feb)|(February)|(Mar)|(March)|(Apr)|(April)|(May)|(Jun)|(June)|(Jul)|(July)|(Aug)|(August)|(Sep)|(September)|(Oct)|(October)|(Nov)|(November)|(Dec)|(December)))";
	private static final String DATE_PATTERN14 = "(0?[1-9]|[12][0-9]|3[01]).((?i)((Jan)|(January)|(Feb)|(February)|(Mar)|(March)|(Apr)|(April)|(May)|(Jun)|(June)|(Jul)|(July)|(Aug)|(August)|(Sep)|(September)|(Oct)|(October)|(Nov)|(November)|(Dec)|(December)))";
	private static final String DATE_PATTERN15 = "(0?[1-9]|[12][0-9]|3[01])\\s((?i)((Jan)|(January)|(Feb)|(February)|(Mar)|(March)|(Apr)|(April)|(May)|(Jun)|(June)|(Jul)|(July)|(Aug)|(August)|(Sep)|(September)|(Oct)|(October)|(Nov)|(November)|(Dec)|(December)))";
	private static final String DATE_PATTERN16 = "(0?[1-9]|[12][0-9]|3[01])/(0?[1-9])";
	private static final String DATE_PATTERN17 = "(0?[1-9]|[12][0-9]|3[01])-(0?[1-9])";

	private Pattern pattern0;
	private Pattern pattern1;
	private Pattern pattern2;
	private Pattern pattern3;
	private Pattern pattern4;
	private Pattern pattern5;
	private Pattern pattern6;
	private Pattern pattern7;
	private Pattern pattern8;
	private Pattern pattern9;
	private Pattern pattern10;
	private Pattern pattern11;
	private Pattern pattern12;
	private Pattern pattern13;
	private Pattern pattern14;
	private Pattern pattern15;
	private Pattern pattern16;
	private Pattern pattern17;
	private Matcher matcher;

	public DateValidator() {
		pattern0 = Pattern.compile(DATE_PATTERN0);
		pattern1 = Pattern.compile(DATE_PATTERN1);
		pattern2 = Pattern.compile(DATE_PATTERN2);
		pattern3 = Pattern.compile(DATE_PATTERN3);
		pattern4 = Pattern.compile(DATE_PATTERN4);
		pattern5 = Pattern.compile(DATE_PATTERN5);
		pattern6 = Pattern.compile(DATE_PATTERN6);
		pattern7 = Pattern.compile(DATE_PATTERN7);
		pattern8 = Pattern.compile(DATE_PATTERN8);
		pattern9 = Pattern.compile(DATE_PATTERN9);
		pattern10 = Pattern.compile(DATE_PATTERN10);
		pattern11 = Pattern.compile(DATE_PATTERN11);
		pattern12 = Pattern.compile(DATE_PATTERN12);
		pattern13 = Pattern.compile(DATE_PATTERN13);
		pattern14 = Pattern.compile(DATE_PATTERN14);
		pattern15 = Pattern.compile(DATE_PATTERN15);
		pattern16 = Pattern.compile(DATE_PATTERN16);
		pattern17 = Pattern.compile(DATE_PATTERN17);

	}

	/**
	 * Validate date format with regular expression
	 * 
	 * @param date
	 *            date address for validation
	 * @return true valid date format, false invalid date format
	 */
	public boolean validate(final String date) {
		matcher = pattern0.matcher(date);
		if (matcher.matches()) {
			return extractDateTwoDigitYear();
		} else {
			return false;
		}
	}

	public Pattern getPattern0() {
		return pattern0;
	}

	public boolean validate1(final String date) {
		matcher = pattern1.matcher(date);
		if (matcher.matches()) {
			return extractDateTwoDigitYear();
		} else {
			return false;
		}
	}

	public Pattern getPattern1() {
		return pattern1;
	}

	public boolean validate2(final String date) {
		matcher = pattern2.matcher(date);
		if (matcher.matches()) {
			return extractDateTwoDigitYear();
		} else {
			return false;
		}
	}

	public Pattern getPattern2() {
		return pattern2;
	}

	public boolean validate3(final String date) {
		matcher = pattern3.matcher(date);
		if (matcher.matches()) {
			return extractDateTwoDigitYear();
		} else {
			return false;
		}
	}

	public Pattern getPattern3() {
		return pattern3;
	}

	public boolean validate4(final String date) {
		matcher = pattern4.matcher(date);
		if (matcher.matches()) {
			return extractDateNoYear();
		} else {
			return false;
		}
	}

	public Pattern getPattern4() {
		return pattern4;
	}

	public boolean validate5(final String date) {
		matcher = pattern5.matcher(date);
		if (matcher.matches()) {
			return extractDateNoYear();
		} else {
			return false;
		}
	}

	public Pattern getPattern5() {
		return pattern5;
	}

	public boolean validate6(final String date) {
		matcher = pattern6.matcher(date);
		if (matcher.matches()) {
			return extractDateFromSpecialYearGroup();
		} else {
			return false;
		}
	}

	public Pattern getPattern6() {
		return pattern6;
	}

	public boolean validate7(final String date) {
		matcher = pattern7.matcher(date);
		if (matcher.matches()) {
			return extractDateFromSpecialYearGroup();
		} else {
			return false;
		}
	}

	public Pattern getPattern7() {
		return pattern7;
	}

	public boolean validate8(final String date) {
		matcher = pattern8.matcher(date);
		if (matcher.matches()) {
			return extractDateFourDigitYear();
		} else {
			return false;
		}
	}

	public Pattern getPattern8() {
		return pattern8;
	}

	public boolean validate9(final String date) {
		matcher = pattern9.matcher(date);
		if (matcher.matches()) {
			return extractDateFourDigitYear();
		} else {
			return false;
		}
	}

	public Pattern getPattern9() {
		return pattern9;
	}

	public boolean validate10(final String date) {
		matcher = pattern10.matcher(date);
		if (matcher.matches()) {
			return extractDateFromSpecialYearGroup();
		} else {
			return false;
		}
	}

	public Pattern getPattern10() {
		return pattern10;
	}

	public boolean validate11(final String date) {

		matcher = pattern11.matcher(date);
		if (matcher.matches()) {
			return extractDateFourDigitYear();
		} else {
			return false;
		}
	}

	public Pattern getPattern11() {
		return pattern11;
	}

	public boolean validate12(final String date) {
		matcher = pattern12.matcher(date);
		if (matcher.matches()) {
			return extractDateNoYearSpecial();
		} else {
			return false;
		}
	}

	public Pattern getPattern12() {
		return pattern12;
	}

	public boolean validate13(final String date) {
		matcher = pattern13.matcher(date);
		if (matcher.matches()) {
			return extractDateNoYearSpecial();
		} else {
			return false;
		}
	}

	public Pattern getPattern13() {
		return pattern13;
	}

	public boolean validate14(final String date) {
		matcher = pattern14.matcher(date);
		if (matcher.matches()) {
			return extractDateNoYearSpecial();
		} else {
			return false;
		}
	}

	public Pattern getPattern14() {
		return pattern14;
	}

	public boolean validate15(final String date) {
		matcher = pattern15.matcher(date);
		if (matcher.matches()) {
			return extractDateNoYearSpecial();
		} else {
			return false;
		}
	}

	public Pattern getPattern15() {
		return pattern15;
	}

	public boolean validate16(final String date) {
		matcher = pattern16.matcher(date);
		if (matcher.matches()) {
			return extractDateNoYear();
		} else {
			return false;
		}
	}

	public Pattern getPattern16() {
		return pattern16;
	}

	public boolean validate17(final String date) {
		matcher = pattern17.matcher(date);
		if (matcher.matches()) {
			return extractDateNoYear();
		} else {
			return false;
		}
	}

	public Pattern getPattern17() {
		return pattern17;
	}

	private boolean checkValidate(String day, String month, int year) {
		if (isDateWithMonthInvalid(day, month)) {
			return false;
		} else if (isFebruary(month)) {
			return checkLeapYear(day, year);
		} else {
			return true;
		}
	}

	private boolean checkValidateString(String day, String month, int year) {
		if (isDateWithMonthInvalidString(day, month)) {
			return false;
		} else if (isFebruaryString(month)) {
			return checkLeapYear(day, year);
		} else {
			return true;
		}
	}

	private boolean isDateWithMonthInvalid(String day, String month) {
		return day.equals("31") && monthsWithout31Days(month);
	}

	private boolean isDateWithMonthInvalidString(String day, String month) {
		return day.equals("31") && monthsWithout31DaysString(month);
	}

	private boolean isFebruary(String month) {
		return month.equals("2") || month.equals("02");
	}

	private boolean isFebruaryString(String month) {
		return month.equalsIgnoreCase("Feb")
				|| month.equalsIgnoreCase("Februry");
	}

	private boolean monthsWithout31Days(String month) {
		return month.equals("4") || month.equals("6") || month.equals("9")
				|| month.equals("11") || month.equals("04")
				|| month.equals("06") || month.equals("09");
	}

	private boolean monthsWithout31DaysString(String month) {
		return month.equalsIgnoreCase("Apr") || month.equalsIgnoreCase("Jun")
				|| month.equalsIgnoreCase("Sep")
				|| month.equalsIgnoreCase("Nov")
				|| month.equalsIgnoreCase("April")
				|| month.equalsIgnoreCase("June")
				|| month.equalsIgnoreCase("September")
				|| month.equalsIgnoreCase("November");
	}

	private boolean checkLeapYear(String day, int year) {
		if (year % 4 == 0) {
			return checkDateWithLeapYear(day);
		} else {
			return checkDateWithoutLeapYear(day);
		}
	}

	private boolean checkDateWithoutLeapYear(String day) {
		if (day.equals("29") || day.equals("30") || day.equals("31")) {
			return false;
		} else {
			return true;
		}
	}

	private boolean checkDateWithLeapYear(String day) {
		if (day.equals("30") || day.equals("31")) {
			return false;
		} else {
			return true;
		}
	}

	private boolean extractDateTwoDigitYear() {
		matcher.reset();
		if (matcher.find()) {
			String day = matcher.group(1);
			String month = matcher.group(2);
			int year = Integer.parseInt(matcher.group(3));
			year = updateToYear(year);
			return checkValidate(day, month, year);
		} else {
			return false;
		}
	}

	private boolean extractDateNoYear() {
		matcher.reset();
		if (matcher.find()) {
			String day = matcher.group(1);
			String month = matcher.group(2);
			int year = DEFAULT_YEAR;
			return checkValidate(day, month, year);
		} else {
			return false;
		}
	}

	private boolean extractDateFromSpecialYearGroup() {
		matcher.reset();
		if (matcher.find()) {
			String day = matcher.group(1);
			String month = matcher.group(2);
			int year = Integer.parseInt(matcher.group(SPECIAL_YEAR_GROUP));
			year = year + YEAR_TO_ADD;
			return checkValidateString(day, month, year);
		} else {
			return false;
		}
	}

	private boolean extractDateFourDigitYear() {
		matcher.reset();
		if (matcher.find()) {
			String day = matcher.group(1);
			String month = matcher.group(2);
			int year = Integer.parseInt(matcher.group(SPECIAL_YEAR_GROUP));
			year = updateToYear(year);
			return checkValidateString(day, month, year);
		} else {
			return false;
		}
	}

	private boolean extractDateNoYearSpecial() {
		matcher.reset();
		if (matcher.find()) {
			String day = matcher.group(1);
			String month = matcher.group(2);
			int year = DEFAULT_YEAR;
			return checkValidateString(day, month, year);
		} else {
			return false;
		}
	}

	private int updateToYear(int year) {
		if (year < 100) {
			year = year + YEAR_TO_ADD;
		}
		return year;
	}
}